package com.lagou.edu.factory;

import com.alibaba.druid.util.StringUtils;
import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Service;
import com.lagou.edu.annotation.Transactional;
import com.lagou.edu.utils.ClassUtil;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ClassName: ClassPathAnnotationApplicationContext
 * @Description: 基于注解的应用上下文BeanFactory(ioc容器）
 * @Author: yuanlei
 * @Date: 2020/5/10 23:01
 */
public class ClassPathAnnotationApplicationContext {
    // 扫包范围
    private String packageName;
    public static ConcurrentHashMap<String, Object> initBean = new ConcurrentHashMap<>();

    public ClassPathAnnotationApplicationContext(String packageName) {
        this.packageName = packageName;
    }

    // 使用beanID查找对象
    public Object getBean(String beanId) throws Exception {
        // 1.使用反射机制获取该包下所有的类已经存在bean的注解类
        List<Class> listClassesAnnotation = findClassExisService();
        if (listClassesAnnotation == null || listClassesAnnotation.isEmpty()) {
            throw new Exception("没有需要初始化的bean");
        }
        // 2.使用Java反射机制初始化对象
        initBean = initBean(listClassesAnnotation);
        if (initBean == null || initBean.isEmpty()) {
            throw new Exception("初始化bean为空!");
        }
        //将含有@Autowired的属性 所对应的对象 使用反射注入
        for(Map.Entry<String, Object> entry: initBean.entrySet()) {
            Object object = entry.getValue();
            attriAssign(object);
        }

        //将含有@Transactional 的类转为cglib代理类 插入事务代码 并重新放入容器中
        for(Map.Entry<String, Object> entry: initBean.entrySet()) {
            Object object = entry.getValue();
            Class classInfo = object.getClass();
            Transactional transactionalAnnotation = (Transactional) classInfo.getAnnotation(Transactional.class);
            if (transactionalAnnotation != null){
                ProxyFactory proxyFactory = (ProxyFactory) initBean.get("proxyFactory");
                Object cglibProxyObject = proxyFactory.getCglibProxy(object);
                initBean.put(entry.getKey(),cglibProxyObject);
            }
        }

        // 3.使用beanID查找查找对应bean对象
        Object object = initBean.get(beanId);
        return object;
    }

    // 使用反射读取类的属性,赋值信息
    public void attriAssign(Object object) throws IllegalArgumentException, IllegalAccessException {
        // 1.获取类的属性是否存在 获取bean注解
        Class<? extends Object> classInfo = object.getClass();
        Field[] declaredFields = classInfo.getDeclaredFields();
        for (Field field : declaredFields) {
            Autowired autowiredAnnotation = field.getDeclaredAnnotation(Autowired.class);
            //如果该属性上含有@Autowired 标志 则将该属性对应的实例对象注入
            if (autowiredAnnotation != null){
                // 属性名称
                String name = field.getName();
                // 2.使用属性名称查找bean容器赋值
                Object bean = initBean.get(name);
                if (bean != null) {
                    // 私有访问允许访问
                    field.setAccessible(true);
                    // 给属性赋值
                    field.set(object, bean);
                    continue;
                }
            }
        }

    }

    // 使用反射机制获取该包下所有的类已经存在bean的注解类
    public List<Class> findClassExisService() throws Exception {
        // 1.使用反射机制获取该包下所有的类
        if (StringUtils.isEmpty(packageName)) {
            throw new Exception("扫包地址不能为空!");
        }
        // 2.使用反射技术获取当前包下所有的类
        List<Class<?>> classesByPackageName = ClassUtil.getClasses(packageName);
        // 3.存放类上有bean注入注解
        List<Class> exisClassesAnnotation = new ArrayList<Class>();
        // 4.判断该类上属否存在注解
        for (Class classInfo : classesByPackageName) {
            Service service = (Service) classInfo.getDeclaredAnnotation(Service.class);
            if (service != null) {
                exisClassesAnnotation.add(classInfo);
                continue;
            }
        }
        return exisClassesAnnotation;
    }

    // 初始化bean对象
    public ConcurrentHashMap<String, Object> initBean(List<Class> listClassesAnnotation)
            throws InstantiationException, IllegalAccessException {
        for (Class classInfo : listClassesAnnotation) {
            // 初始化对象
            Object newInstance = classInfo.newInstance();
            Service service = (Service) classInfo.getDeclaredAnnotation(Service.class);
            String value = service.value();
            // 获取类的简写名称
            String beanId = toLowerCaseFirstOne(classInfo.getSimpleName());
            if (!StringUtils.isEmpty(value)){
                beanId = value;
            }

//            //将标有@Autowired的属性 所对应的对象注入相应属性中 使用反射读取类的属性,赋值信息
//            attriAssign(newInstance);
            initBean.put(beanId, newInstance);
        }
        return initBean;
    }

    // 首字母转小写
    public static String toLowerCaseFirstOne(String s) {
        if (Character.isLowerCase(s.charAt(0)))
            return s;
        else
            return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
    }

}
